package tt.dz.egobus.servicemanager.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import tt.dz.egobus.api.exception.ResponseException;
import tt.dz.egobus.servicemanager.business.key.DriverStatus;
import tt.dz.egobus.servicemanager.business.point.BusPoint;
import tt.dz.egobus.servicemanager.core.lock.CacheLock;
import tt.dz.egobus.servicemanager.entities.DriverRegisterBusRelative;
import tt.dz.egobus.servicemanager.service.BusinessServiceInfoService;
import tt.dz.egobus.servicemanager.service.GrabRedisBusinessWorkCacheService;

import java.util.List;

import static tt.dz.egobus.bean.I18NCodeEnum.RPC_PARAM_ILLEGAL_ERROR;
import static tt.dz.egobus.bean.ServiceManagerI18NCodeEnum.RPC_DRIVER_INVALIDATE_STATUS_ERROR;

/**
 * @author Anson
 * @date 17-8-9
 * @name 抢单模式下的业务工作的缓存处理
 * @since 1.0.6
 */
@Service("grabRedisBusinessWorkCacheServiceImpl")
public class GrabRedisBusinessWorkCacheServiceImpl extends RedisBusinessWorkCacheServiceImpl implements GrabRedisBusinessWorkCacheService {


    private static final Logger LOGGER = LoggerFactory.getLogger(GrabRedisBusinessWorkCacheServiceImpl.class);

    @Autowired
    @Qualifier("businessServiceInfoServiceImpl")
    BusinessServiceInfoService serviceInfoService;

    /**
     * @param driverId
     * @param businessCodes
     * @author Anson
     * @date 17-8-29
     * @name 激活上线, 并且进行预约日期的存储
     * @since 1.0.7
     */
    @Override
    public void active(String driverId, int[] businessCodes) {
        List<String> bespeakDates = serviceInfoService.queryDriverBespeakUnDoneDates(driverId);
        DriverStatus status = this.getStatus(driverId);
        DriverRegisterBusRelative.RelativeStatus relativeStatus = DriverRegisterBusRelative.RelativeStatus.ACTIVE;
        if (status == null) {
            status = new DriverStatus();
        }
        status.setBusinessId("");
        status.setStatus(relativeStatus);
        status.setCacheKey(this.driverStatusKey(driverId, relativeStatus));
        status.setBespeaks(bespeakDates);
        BusPoint busPoint = getNowPoint(driverId);
        double x = 0D;
        double y = 0D;
        if (busPoint != null) {
            x = busPoint.x();
            y = busPoint.y();
        }
        busPoint = new BusPoint(driverId, x, y, businessCodes);
        this.cacheService.set(status.getCacheKey(), busPoint.toJson());
    }


    @Override
    public boolean doGrabBusiness(String driverId, String businessId) {
        if (StringUtils.isEmpty(driverId)) {
            throw new ResponseException(RPC_PARAM_ILLEGAL_ERROR);
        }
        CacheLock lock = new RedisCacheLock(this.cacheService, driverId);
        try {
            int count = 10;
            boolean isLock = false;
            while (((--count) > 0) && !(isLock = lock.aquireLock())) {
                //小于100次并且当前并未获取到锁的时候
                //休眠10毫秒在请求
                Thread.sleep(10);
            }
            if (!isLock) {
                LOGGER.error("司机抢单流程控制获取并发锁失败,driverId={}", driverId);
                return false;
            }
            DriverStatus busStatus = this.getStatus(driverId);
            if (busStatus == null) {
                return false;
            }
            //再次确认下当前司机状态是否是处于激活的状态
            //若多线程下，当前的司机很有可能被别的线程所修改，则当前先的状态可能不是预先的结果
            if (!DriverRegisterBusRelative.RelativeStatus.assertActive(busStatus.getStatus())) {
                throw new ResponseException(RPC_DRIVER_INVALIDATE_STATUS_ERROR);
            }
            busStatus.setBusinessId(businessId);
            String oldKey = busStatus.getCacheKey();
            busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.GRAB_BUSINESS);
            String key = busStatus.getCacheKey();
            this.updateKey(oldKey, key, true);
            return true;
        } catch (Exception e) {
            LOGGER.error("司机多线程下，裂变状态时候的异常", e);
            return false;
        } finally {
            lock.unlock();
        }
    }


    @Override
    public boolean doGrabFail(String driverId) {
        if (StringUtils.isEmpty(driverId)) {
            throw new ResponseException(RPC_PARAM_ILLEGAL_ERROR);
        }
        CacheLock lock = new RedisCacheLock(this.cacheService, driverId);
        try {
            int count = 10;
            boolean isLock = false;
            while (((--count) > 0) && !(isLock = lock.aquireLock())) {
                //小于100次并且当前并未获取到锁的时候
                //休眠10毫秒在请求
                Thread.sleep(10);
            }
            if (!isLock) {
                LOGGER.error("司机抢单失败->状态恢复到激活状态driverId={}", driverId);
                return false;
            }
            DriverStatus busStatus = this.getStatus(driverId);
            if (busStatus == null) {
                return false;
            }
            //再次确认下当前司机状态是否存于抢单状态或者激活状态的时候
            //若多线程下，当前的司机很有可能被别的线程所修改，则当前先的状态可能不是预先的结果
            if (!(DriverRegisterBusRelative.RelativeStatus.assertActive(busStatus.getStatus())
                    || DriverRegisterBusRelative.RelativeStatus.assertGrabBusiness(busStatus.getStatus()))) {
                throw new ResponseException(RPC_DRIVER_INVALIDATE_STATUS_ERROR);
            }
            if (DriverRegisterBusRelative.RelativeStatus.assertActive(busStatus.getStatus())) {
                //当前是处于激活状态的时候
                return true;
            }
            busStatus.setBusinessId("");
            String oldKey = busStatus.getCacheKey();
            busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.ACTIVE);
            String key = busStatus.getCacheKey();
            this.updateKey(oldKey, key, true);
            return true;
        } catch (Exception e) {
            LOGGER.error("司机多线程下，裂变状态时候的异常", e);
            return false;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean giveUpGrab(String driverId) {
        if (StringUtils.isEmpty(driverId)) {
            throw new ResponseException(RPC_PARAM_ILLEGAL_ERROR);
        }
        CacheLock lock = new RedisCacheLock(this.cacheService, driverId);
        try {
            int count = 10;
            boolean isLock = false;
            while (((--count) > 0) && !(isLock = lock.aquireLock())) {
                //小于100次并且当前并未获取到锁的时候
                //休眠10毫秒在请求
                Thread.sleep(10);
            }
            if (!isLock) {
                LOGGER.error("司机抢单失败->状态恢复到激活状态driverId={}", driverId);
                return false;
            }
            DriverStatus busStatus = this.getStatus(driverId);
            if (busStatus == null) {
                return false;
            }
            //再次确认下当前司机状态是否存于抢单状态
            if (!(DriverRegisterBusRelative.RelativeStatus.assertGrabBusiness(busStatus.getStatus()))) {
                throw new ResponseException(RPC_DRIVER_INVALIDATE_STATUS_ERROR);
            }
            String oldKey = busStatus.getCacheKey();
            busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.ACTIVE);
            String key = busStatus.getCacheKey();
            this.updateKey(oldKey, key, true);
            return true;
        } catch (Exception e) {
            LOGGER.error("司机多线程下，裂变状态时候的异常", e);
            return false;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean assertDriverBusinessId(String driverId, String businessId) {
        if (StringUtils.isAnyEmpty(driverId, businessId)) {
            return false;
        }
        DriverStatus driverStatus = this.getStatus(driverId);
        if (driverStatus == null) {
            return false;
        }
        return StringUtils.defaultString(driverStatus.getBusinessId(), "").trim().equals(businessId);
    }

    @Override
    public boolean isContainBespeakDate(String driverId, String bespeakDateStr) {
        DriverStatus driverStatus = this.getStatus(driverId);
        List<String> bespeakDates = driverStatus.getBespeaks();
        return bespeakDates.contains(bespeakDateStr);
    }

    @Override
    public boolean addBespeakDate(String driverId, String bespeakDateStr) {
        DriverStatus driverStatus = this.getStatus(driverId);
        List<String> bespeakDates = driverStatus.getBespeaks();
        if (bespeakDates.contains(bespeakDateStr)) {
            return false;
        }
        bespeakDates.add(bespeakDateStr);
        driverStatus.setBespeaks(bespeakDates);
        this.updateDriverStatusCache(driverStatus, driverId);
        return true;
    }

    @Override
    public boolean deleteBespeakDate(String driverId, String bespeakDateStr) {
        DriverStatus driverStatus = this.getStatus(driverId);
        if (driverStatus == null) {
            //司机下线了
            return true;
        }
        List<String> bespeakDates = driverStatus.getBespeaks();
        bespeakDates.remove(bespeakDateStr);
        driverStatus.setBespeaks(bespeakDates);
        this.updateDriverStatusCache(driverStatus, driverId);
        return true;
    }

    @Override
    public boolean updateBespeakDate(String driverId, String bespeakDateOldStr, String bespeakNewDateStr) {
        DriverStatus driverStatus = this.getStatus(driverId);
        List<String> bespeakDates = driverStatus.getBespeaks();
        bespeakDates.remove(bespeakDateOldStr);
        bespeakDates.add(bespeakNewDateStr);
        this.updateDriverStatusCache(driverStatus, driverId);
        return true;
    }


    @Override
    public boolean doBespeakGrabBusinessSuccess(String driverId) {
        if (StringUtils.isEmpty(driverId)) {
            throw new ResponseException(RPC_PARAM_ILLEGAL_ERROR);
        }
        CacheLock lock = new RedisCacheLock(this.cacheService, driverId);
        try {
            int count = 100;
            boolean isLock = false;
            while (((--count) > 0) && !(isLock = lock.aquireLock())) {
                //小于100次并且当前并未获取到锁的时候
                //休眠10毫秒在请求
                Thread.sleep(10);
            }
            if (!isLock) {
                LOGGER.error("抢单模式预约单抢单->获取并发锁失败,driverId={}", driverId);
                return false;
            }
            LOGGER.info("抢单模式预约单抢单->获取并发锁成功,driverId={}", driverId);
            DriverStatus busStatus = this.getStatus(driverId);
            if (busStatus == null) {
                return false;
            }
            //若多线程下，当前的司机很有可能被别的线程所修改，则当前先的状态可能不是预先的结果
            //当前司机状态允许存在的状态为激活(放弃抢单,但系统可以派单),抢单(当前司机自己抢单)
            if (!(DriverRegisterBusRelative.RelativeStatus.assertActive(busStatus.getStatus())
                    || DriverRegisterBusRelative.RelativeStatus.assertGrabBusiness(busStatus.getStatus()))) {
                throw new ResponseException(RPC_DRIVER_INVALIDATE_STATUS_ERROR);
            }
            String oldKey = busStatus.getCacheKey();
            busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.ACTIVE);
            String key = busStatus.getCacheKey();
            this.updateKey(oldKey, key, true);
            return true;
        } catch (Exception e) {
            LOGGER.error("司机多线程下，裂变状态时候的异常", e);
            return false;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean driverCancelBusiness(String driverId) {
        DriverStatus busStatus = this.getStatus(driverId);
        if (busStatus == null) {
            return false;
        }
        String oldKey = busStatus.getCacheKey();
        busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.ACTIVE);
        String key = busStatus.getCacheKey();
        this.updateKey(oldKey, key, true);
        return true;
    }

    @Override
    public boolean bespeakToBusiness(String driverId, String businessId) {
        if (StringUtils.isEmpty(driverId)) {
            throw new ResponseException(RPC_PARAM_ILLEGAL_ERROR);
        }
        CacheLock lock = new RedisCacheLock(this.cacheService, driverId);
        try {
            int count = 10;
            boolean isLock = false;
            while (((--count) > 0) && !(isLock = lock.aquireLock())) {
                //小于100次并且当前并未获取到锁的时候
                //休眠10毫秒在请求
                Thread.sleep(10);
            }
            if (!isLock) {
                LOGGER.error("司机抢单流程控制获取并发锁失败,driverId={}", driverId);
                return false;
            }
            DriverStatus busStatus = this.getStatus(driverId);
            if (busStatus == null) {
                return false;
            }
            //再次确认下当前司机状态是否是处于激活的状态
            //若多线程下，当前的司机很有可能被别的线程所修改，则当前先的状态可能不是预先的结果
            if (!DriverRegisterBusRelative.RelativeStatus.assertActive(busStatus.getStatus())) {
                throw new ResponseException(RPC_DRIVER_INVALIDATE_STATUS_ERROR);
            }
            busStatus.setBusinessId(businessId);
            String oldKey = busStatus.getCacheKey();
            busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.BEGIN_BUSINESS);
            String key = busStatus.getCacheKey();
            this.updateKey(oldKey, key, true);
            return true;
        } catch (Exception e) {
            LOGGER.error("司机多线程下，裂变状态时候的异常", e);
            return false;
        } finally {
            lock.unlock();
        }
    }


    /**
     * @param driverId
     * @author Anson
     * @date 17-3-27
     * @name 抢单模式下的先获取当前的司机端的状态缓存，判断当前缓存是否存在
     * 若存在，则更新当前状态为{@link DriverRegisterBusRelative.RelativeStatus#BEGIN_BUSINESS}，反之不处理
     * 状态更新则代表point的缓存key也的更新
     * 关心上一个状态是 {@link DriverRegisterBusRelative.RelativeStatus#GRAB_BUSINESS} 以及
     * 激活状态 {@link DriverRegisterBusRelative.RelativeStatus#ACTIVE}
     * @since 1.0.3
     */
    @Override
    public boolean doDriverBeginBusiness(String driverId) throws ResponseException {
        if (StringUtils.isEmpty(driverId)) {
            throw new ResponseException(RPC_PARAM_ILLEGAL_ERROR);
        }
        CacheLock lock = new RedisCacheLock(this.cacheService, driverId);
        try {
            int count = 100;
            boolean isLock = false;
            while (((--count) > 0) && !(isLock = lock.aquireLock())) {
                //小于100次并且当前并未获取到锁的时候
                //休眠10毫秒在请求
                Thread.sleep(10);
            }
            if (!isLock) {
                LOGGER.error("抢单模式开始行程->获取并发锁失败,driverId={}", driverId);
                return false;
            }
            LOGGER.info("抢单模式开始行程->获取并发锁成功,driverId={}", driverId);
            DriverStatus busStatus = this.getStatus(driverId);
            if (busStatus == null) {
                return false;
            }
            //若多线程下，当前的司机很有可能被别的线程所修改，则当前先的状态可能不是预先的结果
            //当前司机状态允许存在的状态为激活(放弃抢单,但系统可以派单),抢单(当前司机自己抢单)
            if (!(DriverRegisterBusRelative.RelativeStatus.assertActive(busStatus.getStatus())
                    || DriverRegisterBusRelative.RelativeStatus.assertGrabBusiness(busStatus.getStatus()))) {
                throw new ResponseException(RPC_DRIVER_INVALIDATE_STATUS_ERROR);
            }
            String oldKey = busStatus.getCacheKey();
            busStatus = this.updateStatus(busStatus, driverId, DriverRegisterBusRelative.RelativeStatus.BEGIN_BUSINESS);
            String key = busStatus.getCacheKey();
            this.updateKey(oldKey, key, true);
            return true;
        } catch (Exception e) {
            LOGGER.error("司机多线程下，裂变状态时候的异常", e);
            return false;
        } finally {
            lock.unlock();
        }
    }

    /**
     * @param driverId     司机id
     * @param status       预先更新的状态
     * @param bespeakDates 预约日期列表
     * @author Anson
     * @date 17-3-27
     * @name 更新当前状态 并增加了预约日期列表
     * @since 1.0.2
     */
    protected DriverStatus updateStatus(String driverId, DriverRegisterBusRelative.RelativeStatus status, List<String> bespeakDates) {
        DriverStatus driverStatus = this.getStatus(driverId);
        if (driverStatus == null) {
            driverStatus = new DriverStatus();
        }
        driverStatus.setStatus(status);
        driverStatus.setCacheKey(this.driverStatusKey(driverId, status));
        driverStatus.setBespeaks(bespeakDates);
        this.updateDriverStatusCache(driverStatus, driverId);
        return driverStatus;
    }
}
